/**
 * 
 */
package jvs.peer.xmpp;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import jvs.peer.io.File;
import jvs.peer.io.MessageConstant;
import jvs.peer.io.OnetimeFile;
import jvs.peer.util.Log;

import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.json.JSONObject;

import android.os.Environment;
import android.os.StatFs;

/**
 * @author qiangli
 * 
 */
public class XmppFileTransferListener implements PacketFilter, PacketListener {
	public static DateFormat df = new SimpleDateFormat("yyyy-MM-dd.hh:mm:ss");
	private static final String crlf = "\r\n";
	private XMPPConnection conn;
	private FileTransferManager ftm;
	private XmppFileTransfer xft;

	private java.io.File cache = null; 

	private XmppWebAdapter adapter = null;
	
	public XmppFileTransferListener(XMPPConnection conn, XmppWebAdapter adapter, java.io.File cache) {
		this.conn = conn;
		this.adapter = adapter;
		
		if (cache != null) {
			this.cache = cache;
			this.cache.mkdirs();
		}
	}

	public boolean accept(Packet packet) {
		if (XmppUtil.isLoopback(packet)) {
			return false;
		}
		if (!(packet instanceof Message)) {
			return false;
		}

		Message msg = (Message) packet;
		if (!msg.getType().equals(Message.Type.normal)) {
			return false;
		}

		String tid = msg.getThread();
		String from = msg.getFrom();
		if (tid == null || from == null) {
			return false;
		}

		Object obj = packet.getProperty(MessageConstant.FTP);

		if (Log.DEBUG) {
			Log.log(this,"accepting id: " + tid + " from: " + from + " to: "
				+ packet.getTo());
		}

		if (obj == null) {
			return false;
		}

		return true;
	}

	public void processPacket(Packet packet) {
		Message msg = (Message) packet;
		String line = msg.getBody();
		try {
			ensureConnection();
			
			ensureSpace();
			//
			// put uri 
			// get uri
			
			String[] pa = split(line, 2);
			String cmd = pa[0].toUpperCase();
			String args = pa[1];
			
			String from = msg.getFrom();
			
			if (Log.DEBUG) {
				Log.log(this, "********from peer: " + from + " ########cmd: " + cmd + " @@@@@@@@args: " + args + " line: " + line );
			}
			String uri = URLDecoder.decode(args, "UTF-8");
			
			//
			//
			if (adapter == null) {
				if (Log.DEBUG) {
					Log.log(this, "web adapter not set");
				}
				return;
			}
			java.io.File temp = createTempFile(from, uri);
			//
			if (cmd.equals("GET")) {
				FileOutputStream os = new FileOutputStream(temp);
				try {
					JSONObject json = new JSONObject();
					json.put("peer", from);
					json.put("uri", uri);
					adapter.handle(json, os);
				} finally {
					os.close();
				}
				File file = new OnetimeFile(temp);
				String desc = temp.getPath();
				xft.send(from, desc, file);
			} else if (cmd.equals("PUT")) {
				//disable for now
				//xft.receive(peer, file.getName(), file);
			} else {
				ok(msg, "Not supported " + line);
			}
		} catch (Exception e) {
			xft = null;
			ftm = null;
			//
			err(msg, e + ":" + line);
			
			e.printStackTrace();
		}
	}

	private void ensureSpace() {
		java.io.File sdcard = Environment.getExternalStorageDirectory();
		StatFs stat = new StatFs(sdcard.getPath());
		double avail = stat.getAvailableBlocks() * stat.getBlockSize();
		double total = stat.getBlockCount() * stat.getBlockSize();
		
		double threshhold = 0.1d;
		if (avail == 0 || total == 0 || avail/total < threshhold ) {
			throw new RuntimeException("Space is low (less than 10%): " + (avail/1024) + "K/"+ (total/1024) + "K");
		}
	}

	private java.io.File createTempFile(String peer, String uri) throws IOException {
		java.io.File dir = new java.io.File(cache, "jvs.peer.xmpp/" + peer + "/");
		dir.mkdirs();
		java.io.File f = java.io.File.createTempFile("xft", ".tmp", dir);
		return f;
	}

	private String[] split(String s, int limit) {
		if (s == null) {
			return new String[0];
		}
		s = s.trim();
		if (s.length() == 0) {
			return new String[0];
		}
		String[] sa = s.split("\\s+", limit);
		return sa;
	}

	private void status(Message msg, String code, String s) {
		replyMessage(msg, code + " " + s + crlf);
	}

	private void ok(Message msg, String s) {
		status(msg, "200", s);
	}

	private void err(Message msg, String s) {
		status(msg, "500", s);
	}

	void format(StringBuilder sb, File f) {
		format(sb, f.isDirectory(), f.canRead(), f.canWrite(), f.canExecute(), f.length(), f.lastModified(), f.getName());
	}
	
	private void format(StringBuilder sb, boolean isDirectory, boolean canRead,
			boolean canWrite, boolean canExecute, long length, long modified,
			String name) {
		sb.append(isDirectory ? "d" : "-");
		sb.append(canRead ? "r" : "-");
		sb.append(canWrite ? "w" : "-");
		sb.append(canExecute ? "x" : "-");
		sb.append("\t");
		sb.append("?\t");
		sb.append("?\t");
		sb.append(length + "");
		sb.append("\t");
		try {
			sb.append(df.format(new Date(modified)));
		} catch (Exception e) {
			e.printStackTrace();
		}
		sb.append("\t");
		sb.append(name);
		sb.append(isDirectory ? "/" : "");
	}

	synchronized private void ensureConnection() {
		if (conn.isConnected()) {
			if (xft == null) {
				ftm = new FileTransferManager(conn);
				xft = new XmppFileTransfer(ftm);
			}
			return;
		}
		throw new RuntimeException("Not connected, try again later.");
	}

	private void replyMessage(Message in, String message) {
		Message msg = new Message();
		msg.setThread(in.getThread());
		msg.setTo(in.getFrom());
		msg.setType(in.getType());
		msg.setBody(message);
		msg.setFrom(in.getTo());
		conn.sendPacket(msg);
	}
}
